﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using FileTransferProtocol;
using Fcdbas.Core.Net;
using Fcdbas.Core.Util;
using Fcdbas.Core.Xml;
using System.Net;
using System.IO;


namespace FileTransferClient
{
    public partial class FormClient : Form
    {
        private List<TFileInfo> FileSendList = new List<TFileInfo>();
        private List<TFileInfo> FileDownLoadList = new List<TFileInfo>();
        private List<TransferHead> TSendHeadList = new List<TransferHead>();
        private List<TransferHead> TDownLoadHeadList = new List<TransferHead>();
        private Dictionary<string, SocketClient> FSendClientList = new Dictionary<string, SocketClient>();
        private Dictionary<string, SocketClient> FDownLoadClientList = new Dictionary<string, SocketClient>();
        private ProxyInfo proxyInfo = null;
        
        private Config config;
        private string DownLoadPathTemp;
        public event OnEventDelegate FEvent;
        private string FileSendConfig;
        private string FileDownLoadConfig;
        public void Event(string eventMessage)
        {
            Event(eventMessage, false);
        }
        public void Event(string eventMessage, bool ex)
        {

            //if (lstMessages.InvokeRequired)
            //{
            //    lstMessages.Invoke(new OnEventDelegate(delegate(string s) { Event(s); }), eventMessage);
            //}
            //else
            //{

            //    if (eventMessage.Contains("Connected"))
            //    {
            //        Interlocked.Increment(ref FConnectionCount);
            //    }

            //    if (eventMessage.Contains("Disconnected"))
            //    {
            //        Interlocked.Decrement(ref FConnectionCount);
            //    }

            //    this.Text = FConnectionCount.ToString() + " Connections";

            //    string[] s = eventMessage.Split('\n');

            //    for (int i = s.Length - 1; i >= 0; i--)
            //    {
            //        lstMessages.BeginUpdate();
            //        lstMessages.Items.Insert(0, s[i]);

            //        if (lstMessages.Items.Count > 100)
            //            lstMessages.Items.RemoveAt(100);

            //        lstMessages.EndUpdate();
            //    }

            //}

        }
        private SocketClientSync client;
        public FormClient()
        {
            InitializeComponent();
            this.FEvent += new OnEventDelegate(FormClient_FEvent);
            
        }

        void FormClient_FEvent(FileBlock eventMessage)
        {
           // bindingSource1.ResetBindings(false);
        }

       
       // private int BlockCount = 5;
        private int SendSize = 8192;
        private TransferHead CreateHead(string filePath)
        {
            TransferHead head = TSendHeadList.Find(p => (p.FileInfo.FilePath + "\\" + p.FileInfo.FileName) == filePath);
            if (head == null)
            {
                head = TransferHead.CreateTransferHead(filePath);
                TSendHeadList.Add(head);
            }
            FileSendList.Add(head.FileInfo);
            bsSendFile.DataSource = FileSendList;
            //gvFileList.DataSource = FileList;
            return head;
        }
        private SocketClientSync CreateSocketClient(string host, int port)
        {
            SocketClientSync client = new SocketClientSync(new IPEndPoint(IPAddress.Parse(host), port));
            client.DelimiterType = DelimiterType.dtMessageTailExcludeOnReceive;
            client.Delimiter = Encoding.GetEncoding(1252).GetBytes("%$#@^&");
            client.EncryptType = EncryptType.etNone;
            client.CompressionType = CompressionType.ctNone;
            client.MessageBufferSize = SendSize * 2;
            client.SocketBufferSize = 4096;
            if (proxyInfo != null)
                client.ProxyInfo = proxyInfo;
            return client;
        }
        class Session
        {
            public SocketClientSync Client
            {
                get;
                set;
            }
            public FileBlock Block
            {
                get;
                set;
            }
            public NetSession NetSession
            {
                get;
                set;
            }
        }
        
        private void btnTransfer_Click(object sender, EventArgs e)
        {
           
            if (openFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                if (FSendClientList.Keys.Contains(openFileDialog1.FileName))
                {
                    MessageBox.Show("选择的文件已经在传输队列中了，请选择其他要传输的文件");
                    return;
                }
                
                if (client == null)
                {
                    client = CreateSocketClient(this.txtHost.Text, int.Parse(txtPort.Text));
                    client.Connect();
                }
                if(!client.Connected)
                    client.Connect();
                if (client.Connected)
                {
                    NetSession session = new NetSession(FileTransferProtocol.ConnectionState.Connected);
                    session.CmdType = FileTransferProtocol.CommandType.UpInit;
                    session.Head = CreateHead(openFileDialog1.FileName);

                    session.State = FileTransferProtocol.ConnectionState.Connected;
                    byte[] buffer = ObjectHelper.SerialzieObject<NetSession>(session);
                    client.Write(buffer);

                    buffer = ReadFromSocket(client);
                    if (buffer == null)
                        return;
                    NetSession sRes = ObjectHelper.DeserialzieObject<NetSession>(buffer);
                    if (sRes.CmdType == FileTransferProtocol.CommandType.UpBegin || sRes.CmdType == FileTransferProtocol.CommandType.UpResume)
                    {
                        if (sRes.CmdType == FileTransferProtocol.CommandType.UpResume)
                        {
                            for (int i = 0; i < session.Head.Blocks.Count; i++)
                            {
                                foreach (FileBlock fb in sRes.Head.Blocks)
                                {
                                    FileBlock tfb = session.Head.Blocks[i];
                                    if (tfb.BlockId == fb.BlockId)
                                    {
                                        tfb.Complated = fb.Complated;
                                        tfb.HasTransfer = fb.HasTransfer;
                                        tfb.StartProcess = false;
                                        break;
                                    }
                                }
                            }
                        }
                        FileSendClient fclient = new FileSendClient(FEvent, session.Head);
                        fclient.AllFinished += new AllFinishedEventHandler(fclient_AllFinished);
                        fclient.BlockFinished += new BlockFinishedEventHandler(fclient_BlockFinished);
                        fclient.TransferSum += new TransferEventHandler(fclient_TransferSum);
                        SocketClient socketClient = new SocketClient(CallbackThreadType.ctIOThread, fclient, DelimiterType.dtMessageTailExcludeOnReceive, Encoding.GetEncoding(1252).GetBytes("%$#@^&"), 1024 * 10, 1024 * 160);
                        for (int i = 0; i < session.Head.Blocks.Count + 2; i++)
                        {
                            socketClient.AddConnector(String.Empty, new IPEndPoint(IPAddress.Parse(txtHost.Text), int.Parse(txtPort.Text)), proxyInfo, EncryptType.etNone, CompressionType.ctNone, null,10,2000);
                        }
                        socketClient.Start();
                        FSendClientList.Add(openFileDialog1.FileName, socketClient);
                        //  client.Disconnect();
                        //for (int i = 0; i < session.Head.Blocks.Count; i++)
                        //{
                        //    Session ses = new Session();
                        //    SocketClientSync socket = CreateSocketClient(this.txtHost.Text, int.Parse(txtPort.Text));
                        //    socket.Connect();
                        //    ses.Client = socket;
                        //    if (ses.Client.Connected)
                        //    {
                        //        //ses.Client = socket;
                        //        ses.Block = session.Head.Blocks[i];
                        //        ses.NetSession = sRes;
                        //        Thread read = new Thread(new ParameterizedThreadStart(ThreadExecute));
                        //        read.Start(ses);
                        //    }
                        //}
                    }
                }
                else
                {
                    MessageBox.Show("连接不到服务器，请联系系统管理员！");
                }
                //try
                //{
                //    client.Disconnect();
                //}
                //catch(Exception ex)
                //{
                //    Console.WriteLine(ex.Message);
                //}
            }
        }
        private void ShowSendStatus(FileTransferClient task)
        {
            TFileInfo fileInfo = FileSendList.Find(p => p.FileName == task.TransferFile.FileInfo.FileName);
            if (fileInfo == null)
                return;
            fileInfo.ComplatedSize = task.ComplatedSize;
            fileInfo.KByteAverSpeed = task.ByteAverSpeed;
            fileInfo.TimePast = task.TimePast.ToString("c");
            fileInfo.TimeRemaining = task.TimeRemaining.ToString("c");
            bsSendFile.DataSource = FileSendList;
            bsSendFile.ResetBindings(false);
        }
        void fclient_TransferSum(object sender, EventArgs e)
        {
            FileSendClient Client = sender as FileSendClient;
            if (Client == null)
                return;
            if (InvokeRequired)
            {

                this.Invoke(new Delegate_Progress(ShowSendStatus), Client);
            }
            else
            {
                
                ShowSendStatus(Client);
            }
        }
        delegate void Delegate_Progress(FileTransferClient task);
        void fclient_BlockFinished(object sender, BlockFinishedEventArgs e)
        {
            
        }
        private void ShowDownLoadStatus(FileTransferClient task)
        {
            TFileInfo fileInfo = FileDownLoadList.Find(p => p.FileName == task.TransferFile.FileInfo.FileName);
            if (fileInfo == null)
                return;
            fileInfo.ComplatedSize = task.ComplatedSize;
            fileInfo.KByteAverSpeed = task.ByteAverSpeed;
            fileInfo.TimePast = task.TimePast.ToString("c");
            fileInfo.TimeRemaining = task.TimeRemaining.ToString("c");
            bsDownLoad.DataSource = FileDownLoadList;
            bsDownLoad.ResetBindings(false);
        }
        delegate void DelegateFinished(string fileName);
        void FileSendFinished(string fileName)
        {
            try
            {
                if (string.IsNullOrEmpty(fileName))
                    return;
                SocketClient client = FSendClientList[fileName];
                if (client == null)
                    return;
                FileSendClient ft = client.SocketService as FileSendClient;
                if (ft != null)
                    ft.Stop = true;
                Thread.Sleep(1000);
                
                //client.Stop();
                TransferHead head = TSendHeadList.Find(p => (p.FileInfo.FilePath + "\\" + p.FileInfo.FileName) == fileName);
                TSendHeadList.Remove(head);
                FSendClientList.Remove(fileName);
                TFileInfo fileInfo = FileSendList.Find(p => (p.FilePath + "\\" + p.FileName) == fileName);
                if (fileInfo != null)
                {
                    FileSendList.Remove(fileInfo);
                    bsSendFile.ResetBindings(false);
                    gvFileList.Refresh();
                }
            }
            catch (Exception ex)
            {
                Fcdbas.Core.Log.XTrace.WriteException(ex, FileSendClient.LogPath);
                Console.WriteLine(ex.Message);
            }
        }

        void FileDownLoadFinished(string fileName)
        {
            try
            {
                if (string.IsNullOrEmpty(fileName))
                    return;
                if (FDownLoadClientList.Keys.Contains(fileName))
                {
                    SocketClient client = FDownLoadClientList[fileName];
                    if (client == null)
                        return;
                    FileDownLoadClient ft = client.SocketService as FileDownLoadClient;
                    if (ft != null)
                        ft.Stop = true;
                    Thread.Sleep(1000);

                }
                //client.Stop();
                TransferHead head = TDownLoadHeadList.Find(p => (p.FileInfo.FilePath + "\\" + p.FileInfo.FileName) == fileName);
                TDownLoadHeadList.Remove(head);
                FDownLoadClientList.Remove(fileName);
                TFileInfo fileInfo = FileDownLoadList.Find(p => (p.FilePath + "\\" + p.FileName) == fileName);
                if (fileInfo != null)
                {
                    FileDownLoadList.Remove(fileInfo);
                    bsDownLoad.ResetBindings(false);
                    gvDownLoad.Refresh();
                }
            }
            catch (Exception ex)
            {
                Fcdbas.Core.Log.XTrace.WriteException(ex, FileDownLoadClient.LogPath);
                Console.WriteLine(ex.Message);
            }
        }
        void fclient_AllFinished(object sender, AllFinishedEventArgs e)
        {
            if (InvokeRequired)
            {

                this.Invoke(new DelegateFinished(FileSendFinished), e.FileName);
            }
            else
            {

                FileSendFinished(e.FileName);
            }
        }
        private void dataGridView1_CellContentClick_1(object sender, DataGridViewCellEventArgs e)
        {

        }
        private string configPath;
        private void FormClient_Load(object sender, EventArgs e)
        {
            configPath = AppDomain.CurrentDomain.BaseDirectory + "Config.xml";

            config = Fcdbas.Core.Xml.XmlHelper.ToXmlFileEntity<Config>(configPath);
            if (config == null)
                config = new Config();
            else
            {
                txtDownLoadFolder.Text = config.DownLoadPath;
                txtHost.Text = config.IP;
                txtPort.Text = config.Port.ToString();
            }
            if (config.ProxyUse != ProxyUse.NoUse)
                proxyInfo = Config.CreateProxy(config);
            DownLoadPathTemp = txtDownLoadFolder.Text + "\\Temp";
            FileSendConfig = AppDomain.CurrentDomain.BaseDirectory + "SendClient.xml";
            FileDownLoadConfig = AppDomain.CurrentDomain.BaseDirectory + "DownLoadClient.xml";
            TSendHeadList = Fcdbas.Core.Xml.XmlHelper.ToXmlFileEntity<List<TransferHead>>(FileSendConfig);
            if (TSendHeadList == null)
                TSendHeadList = new List<TransferHead>();
            TDownLoadHeadList = Fcdbas.Core.Xml.XmlHelper.ToXmlFileEntity<List<TransferHead>>(FileDownLoadConfig);
            if (TDownLoadHeadList == null)
                TDownLoadHeadList = new List<TransferHead>();
        }

        private void FormClient_FormClosing(object sender, FormClosingEventArgs e)
        {

            foreach (SocketClient client in FSendClientList.Values)
            {
                FileSendClient ft = client.SocketService as FileSendClient;
                if (ft != null)
                    ft.Stop = true;
                Thread.Sleep(1000);
              //  client.Stop();
            }
           // Thread.Sleep(1000);
            for (int i = 0; i < TSendHeadList.Count; i++)
            {
                for (int j = 0; j < TSendHeadList[i].Blocks.Count; j++)
                {
                    TSendHeadList[i].Blocks[j].Data = null;
                    if(TSendHeadList[i].Blocks[j].FStream != null)
                    {
                        TSendHeadList[i].Blocks[j].FStream.Close();
                        TSendHeadList[i].Blocks[j].FStream = null;
                    }
                }
            }
            Fcdbas.Core.Xml.XmlHelper.ToXmlFile(TSendHeadList, FileSendConfig);

            foreach (SocketClient client in FDownLoadClientList.Values)
            {
                FileDownLoadClient ft = client.SocketService as FileDownLoadClient;
                if (ft != null)
                    ft.Stop = true;
                Thread.Sleep(1000);
                //  client.Stop();
            }
            // Thread.Sleep(1000);
            for (int i = 0; i < TDownLoadHeadList.Count; i++)
            {
                for (int j = 0; j < TDownLoadHeadList[i].Blocks.Count; j++)
                {
                    TDownLoadHeadList[i].Blocks[j].Data = null;
                    if (TDownLoadHeadList[i].Blocks[j].FStream != null)
                    {
                        //TDownLoadHeadList[i].Blocks[j].FStream.Flush();
                        TDownLoadHeadList[i].Blocks[j].FStream.Close();
                        TDownLoadHeadList[i].Blocks[j].FStream = null;
                    }
                    
                }
            }
            Fcdbas.Core.Xml.XmlHelper.ToXmlFile(TDownLoadHeadList, FileDownLoadConfig);
            Fcdbas.Core.Xml.XmlHelper.ToXmlFile(config, configPath);
            //if (client.Connected)
            //    client.Disconnect();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            //if (client != null && client.Connected)
            //    client.Disconnect();
        }
        private byte[] ReadFromSocket(SocketClientSync client)
        {
            byte[] buffer = client.ReadBytes(3000);
            int tryCount = 0;
            while (buffer == null && tryCount < 3)
            {
                Thread.Sleep(1000);
                buffer = client.ReadBytes(3000);
                tryCount++;
            }
            return buffer;
        }
        private void btnDownLoadFile_Click(object sender, EventArgs e)
        {
            
            if (client == null)
            {
                client = CreateSocketClient(this.txtHost.Text, int.Parse(txtPort.Text));
                client.Connect();
            }
            if (!client.Connected)
                client.Connect();
            if (client.Connected)
            {
                NetSession session = new NetSession(FileTransferProtocol.ConnectionState.Connected);
                session.CmdType = FileTransferProtocol.CommandType.FetchList;
                session.State = FileTransferProtocol.ConnectionState.Connected;
                byte[] buffer = ObjectHelper.SerialzieObject<NetSession>(session);
                client.Write(buffer);

                buffer = ReadFromSocket(client);

                if (buffer == null)
                    return;
                NetSession sRes = ObjectHelper.DeserialzieObject<NetSession>(buffer);
                if (!string.IsNullOrEmpty(sRes.Content))
                {
                    List<TFileInfo> fileList = sRes.Content.ToXmlEntity<List<TFileInfo>>();
                    if (fileList == null)
                    {
                        MessageBox.Show("获取不到下载的文件或者可提供下载的文件为0");
                        return;
                    }
                    dlgSelectDownLoadFile dlg = new dlgSelectDownLoadFile(fileList);
                    if (dlg.ShowDialog(this) == System.Windows.Forms.DialogResult.OK)
                    {
                        foreach (string file in dlg.SelectFiles)
                        {
                            DownLoadFile(file);
                        }

                    }
                }
                else
                {
                    MessageBox.Show("获取不到下载的文件或者可提供下载的文件为0");
                }
            }
            else
            {
                MessageBox.Show("连接不到服务器，请联系系统管理员！");
            }

        }
        private void DownLoadFile(string file)
        {
            NetSession sRes =new NetSession (FileTransferProtocol.ConnectionState.Connected);
            sRes.CmdType = FileTransferProtocol.CommandType.FetchDownFileInfo;
            sRes.Content = file;
            byte[] buffer = ObjectHelper.SerialzieObject<NetSession>(sRes);
            client.Write(buffer);

            buffer = ReadFromSocket(client);
            if (buffer == null)
            {
                MessageBox.Show("获取文件信息失败,请重试！");
                return;
            }
            sRes = ObjectHelper.DeserialzieObject<NetSession>(buffer);
            if (sRes == null)
            {
                MessageBox.Show("数据异常，请联系支持人员");
                return;
            }
            if (sRes.CmdType == FileTransferProtocol.CommandType.DownInit)
            {
                bool resume = false;
                TransferHead head= InitDownLoadHead(sRes.Head, ref resume);
                if (resume)
                {
                    for (int i = 0; i < head.Blocks.Count; i++)
                    {
                        head.Blocks[i].StartProcess = false;
                    }
                }
                FileDownLoadClient DownLoadClient = new FileDownLoadClient(FEvent, head);
                DownLoadClient.AllFinished += new AllFinishedEventHandler(DownLoadClient_AllFinished);
                DownLoadClient.BlockFinished += new BlockFinishedEventHandler(DownLoadClient_BlockFinished);
                DownLoadClient.TransferSum += new TransferEventHandler(DownLoadClient_TransferSum);
                SocketClient socketClient = new SocketClient(CallbackThreadType.ctIOThread, DownLoadClient, DelimiterType.dtMessageTailExcludeOnReceive, Encoding.GetEncoding(1252).GetBytes("%$#@^&"), 1024 * 10, 1024 * 160);
                for (int i = 0; i < sRes.Head.Blocks.Count + 2; i++)
                {
                    socketClient.AddConnector(String.Empty, new IPEndPoint(IPAddress.Parse(txtHost.Text), int.Parse(txtPort.Text)), proxyInfo, EncryptType.etNone, CompressionType.ctNone, null, 10, 2000);
                }
                socketClient.Start();
                FDownLoadClientList.Add(sRes.Head.FileInfo.FilePath + "\\" + sRes.Head.FileInfo.FileName, socketClient);
                FileDownLoadList.Add(sRes.Head.FileInfo);
                bsDownLoad.DataSource = FileDownLoadList;
            }
        }
        private TransferHead InitDownLoadHead(TransferHead tHead, ref bool resume)
        {
            TransferHead th = TDownLoadHeadList.Find(p => p.FileInfo.FileName == tHead.FileInfo.FileName && p.FileInfo.FileSize == tHead.FileInfo.FileSize);
            if (th != null)
            {
                resume = true;
                return th;
            }
            tHead.FileInfo.FilePath = config.DownLoadPath + "\\" + tHead.Guid;
            if (!Directory.Exists(tHead.FileInfo.FilePath))
            {
                Directory.CreateDirectory(tHead.FileInfo.FilePath);
            }
            string tempath = DownLoadPathTemp + "\\" + tHead.Guid;
            if (!Directory.Exists(tempath))
            {
                Directory.CreateDirectory(tempath);
            }
            for (int i = 0; i < tHead.Blocks.Count; i++)
            {
                FileBlock block = tHead.Blocks[i];
                block.TempFile = tempath + "\\" + block.TempFile;
            }

            TDownLoadHeadList.Add(tHead);
            resume = false;
            return tHead;
        }
        void DownLoadClient_TransferSum(object sender, EventArgs e)
        {
            FileDownLoadClient Client = sender as FileDownLoadClient;
            if (Client == null)
                return;
            if (InvokeRequired)
            {

                this.Invoke(new Delegate_Progress(ShowDownLoadStatus), Client);
            }
            else
            {

                ShowDownLoadStatus(Client);
            }
        }

        void DownLoadClient_BlockFinished(object sender, BlockFinishedEventArgs e)
        {
            throw new NotImplementedException();
        }

        void DownLoadClient_AllFinished(object sender, AllFinishedEventArgs e)
        {
            if (InvokeRequired)
            {

                this.Invoke(new DelegateFinished(FileDownLoadFinished), e.FileName);
            }
            else
            {

                FileDownLoadFinished(e.FileName);
            }
        }

        private void btnDownFold_Click(object sender, EventArgs e)
        {
            if (this.folderBrowserDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                txtDownLoadFolder.Text = this.folderBrowserDialog1.SelectedPath;
                config.DownLoadPath = txtDownLoadFolder.Text;
                DownLoadPathTemp = txtDownLoadFolder.Text + "\\Temp";
            }
        }

        private void btnProxy_Click(object sender, EventArgs e)
        {
            dlgProxySetting dlg = new dlgProxySetting(config);
            if(dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                this.proxyInfo = dlg.ProxyInfo;
            }
        }

        private void txtHost_TextChanged(object sender, EventArgs e)
        {
            config.IP = txtHost.Text;
           
        }

        private void txtPort_TextChanged(object sender, EventArgs e)
        {
            config.Port = int.Parse(txtPort.Text);
        }

        private void txtDownLoadFolder_TextChanged(object sender, EventArgs e)
        {
            config.DownLoadPath = txtDownLoadFolder.Text;
        }
    }
}
